Skip to content

refactor(studio): simplify hooks, split contexts, remove dead code#1416

Merged
miguel-heygen merged 23 commits into
mainfrom
refactor/studio-simplification-wave-1
Jun 13, 2026
Merged

refactor(studio): simplify hooks, split contexts, remove dead code#1416
miguel-heygen merged 23 commits into
mainfrom
refactor/studio-simplification-wave-1

Conversation

@miguel-heygen

Copy link
Copy Markdown
Collaborator

Summary

  • 77 files changed, -391 net LOC across the entire studio package
  • Split StudioContext into Shell + Playback to stop playback state re-rendering shell UI
  • Split DomEditContext into Actions + Selection to stop hover re-rendering edit consumers
  • Decomposed 5 god hooks (2,826 → 1,707 LOC in parent files): useGsapScriptCommits, useDomEditSession, useDomEditCommits, useAppHotkeys, useFileManager
  • Created TimelineEditContext to eliminate 15-callback prop drilling chain
  • Deleted 7 dead files, 3 dead store fields, consolidated GSAP duplication into gsapShared.ts
  • Fixed re-render cascades in App.tsx (memoized renderQueue, toolbar, canvas rect)
  • Eliminated 3 effect-chain state mirrors (lint findings, hover clearing, GSAP fetch)
  • Guarded Zustand no-op setters (60/sec during reverse playback) + fixed useConsoleErrorCapture memory leak
  • Deduplicated rounding utils, selector helpers, percentage computation, iframe document access
  • Removed ~40 unsafe type casts with proper typed interfaces

Test plan

  • bun run --cwd packages/studio typecheck passes
  • bun run --cwd packages/studio test — all 796 tests pass
  • Studio loads and basic editing workflow works (select element, edit properties, play/pause, undo/redo)
  • Timeline interactions work (drag, resize, split, keyframes)
  • Gesture recording works
  • Render queue works

miguel-heygen and others added 21 commits June 13, 2026 14:30
…re memory leak

- Guard setIsPlaying to skip set() when value unchanged (eliminates 60
  notifications/sec during reverse playback)
- Guard caption store selectGroup to bail before set() when group missing
  (prevents empty Zustand notifications)
- Guard clearSelection to skip when already empty
- Fix useConsoleErrorCapture: restore original console.error, remove error
  event listener, and delete __hfErrorCapture flag on cleanup
Remove 7 dead files (audioBeatDetection, keyframeSnapping,
timelineInspector, DopesheetStrip, StaggerControls,
TimelineLayerPanel, TimelineEditorNotice) and their test companions.

Delete unused computeFitToChildrenSize export from propertyPanelHelpers.

Fix re-export indirection: useDomEditCommits and studioMotionOps.test
now import patch builders directly from manualEditsDomPatches instead
of the re-export passthrough in manualEditsDom.
…, hover, and GSAP fetch

Move lint findingsByElement sync from App.tsx into useLintModal where
the value is produced, removing the mirroring useEffect. Consolidate
4 hover-clearing effects in useDomSelection into 2 (one unconditional
on context change, one conditional combining caption mode, selection
match, and disconnected element checks). Fold the GSAP retry effect
into the fetch effect in useGsapTweenCache, scheduling a single retry
via setTimeout when the initial fetch returns 0 animations.

Eliminates 3 unnecessary render cycles from effect chains.
… re-render cascade

- Wrap renderQueue object in useMemo so StudioContext consumers don't
  re-render on every App render
- Memoize timelineToolbar JSX so NLELayout memo isn't defeated
- Move canvasRect getBoundingClientRect() from render-time IIFE to a
  useLayoutEffect-backed ref, eliminating layout thrashing
- Track and clear setTimeout handles in refreshPreviewDocumentVersion
  to prevent stale timer accumulation on rapid calls and unmount
…me access, keyframe parsing

Extract duplicated PROPERTY_DEFAULTS, IframeGsap interface, iframe
accessors (getIframeGsap, queryIframeElement), percentage keyframe
parsing, and toAbsoluteTime into a single gsapShared.ts module.

Removes ~120 lines of copy-pasted logic across 8 hook files, reducing
drift risk between the duplicate implementations.
…implification-wave-1

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
…fication-wave-1

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
…io-simplification-wave-1

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
…-simplification-wave-1

Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
…mplification-wave-1

# Conflicts:
#	packages/studio/src/hooks/gsapRuntimePreview.ts
…ification-wave-1

# Conflicts:
#	packages/studio/src/hooks/gsapKeyframeCommit.ts
#	packages/studio/src/hooks/gsapRuntimeBridge.ts
#	packages/studio/src/hooks/gsapRuntimeKeyframes.ts
#	packages/studio/src/hooks/gsapRuntimeReaders.ts
#	packages/studio/src/hooks/gsapShared.ts
#	packages/studio/src/utils/audioBeatDetection.ts
…n hooks

Extract useFileTree (tree loading, refresh, derived assets/compositions)
and useEditorSave (debounced save with history tracking) from the 508-LOC
useFileManager. The parent hook composes both and retains file I/O,
click-to-source, upload/import, and CRUD — preserving the same public
interface so no consumers change.
Extract geometry (path offset, box size, rotation) and element lifecycle
(delete, z-index reorder) into useDomGeometryCommits and
useElementLifecycleOps. Parent keeps persistDomEditOperations as core
and composes all sub-hooks — public interface unchanged.
Replace 15 individual useRef callback refs with a single cbRef object.
Extract keydown dispatch into pure dispatchModifierKey/dispatchPlainKey
functions. Merge duplicate undo/redo logic into shared applyHistory.
Extract cross-origin listener boilerplate into safeAddListener/safeRemoveListener.

Hook body: 204 LOC (down from 445). Public API unchanged.
Extract GSAP-aware geometry intercepts (move/resize/rotation) and
animated property commit into useGsapAwareEditing, and selection
wiring, GSAP cache management, preview sync, and selection handlers
into useDomEditWiring. The parent remains a pure composition shell.
@miguel-heygen miguel-heygen changed the title refactor(studio): architecture simplification wave 1 — contexts, hooks, dedup, dead code refactor(studio): simplify hooks, split contexts, remove dead code Jun 13, 2026
Comment on lines +57 to +59
const response = await fetch(
`/api/projects/${pid}/files/${encodeURIComponent(targetPath)}`,
);
Comment on lines +75 to +82
const removeResponse = await fetch(
`/api/projects/${pid}/file-mutations/remove-element/${encodeURIComponent(targetPath)}`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ target: patchTarget }),
},
);
}
let cancelled = false;
setFileTreeLoaded(false);
fetch(`/api/projects/${projectId}`)
const refreshFileTree = useCallback(async () => {
const pid = projectIdRef.current;
if (!pid) return;
const res = await fetch(`/api/projects/${pid}`);
@miguel-heygen

Copy link
Copy Markdown
Collaborator Author

Splitting into a Graphite stack of 9 smaller PRs for easier review.

@miguel-heygen miguel-heygen reopened this Jun 13, 2026
@miguel-heygen miguel-heygen merged commit 7bff49e into main Jun 13, 2026
47 of 48 checks passed
@miguel-heygen miguel-heygen deleted the refactor/studio-simplification-wave-1 branch June 13, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants